package jwt

import (
	"encoding/json"
	"errors"
	"gitee.com/wuzheng0709/backend-gopkg/infrastructure/connector/redis"
	"gitee.com/wuzheng0709/backend-gopkg/infrastructure/pkg/gin/log"
	"time"

	"gitee.com/wuzheng0709/backend-gopkg/infrastructure/config"
	"github.com/golang-jwt/jwt/v4"
)

// 一些常量
var (
	TokenExpired     = errors.New("Token 已过期")
	TokenNotValidYet = errors.New("Token 未激活")
	TokenMalformed   = errors.New("这不是 Token")
	TokenInvalid     = errors.New("无法解析的 Token")
	SignKey          = []byte(config.C.Jwt.SignKey)
)

// 载荷，可以加一些自己需要的信息
type customClaims struct {
	UID string `json:"uid"`
	jwt.StandardClaims
}
type TokenField struct {
	AppId      string `json:"app_id"`
	UserId     string `json:"user_id"`
	Platform   string `json:"platform"`
	DeviceId   string `json:"device_id"`
	DeviceName string `json:"device_name"`
	UserName   string `json:"user_name"`
	RealName   string `json:"real_name"`
	UserType   string `json:"user_type"`
}

type TokenFieldService interface {
	RetMap() map[string]interface{}
	RetStruct(map[string]string) error
}

//fields := jwt.NewTokenField(
//	jwt.AppId(toolfunc.Int642String(appUser.UserID)),
//	jwt.UserId(toolfunc.Int642String(appUser.UserID)),
//	jwt.Platform(cqe.Platform),
//).RetMap()

func (tf *TokenField) RetMap() map[string]interface{} {
	var tfmap map[string]interface{}
	tfByte, _ := json.Marshal(tf)
	json.Unmarshal(tfByte, &tfmap)
	return tfmap
}

func (tf *TokenField) RetStruct(tfmap map[string]string) error {
	jsonByte, _ := json.Marshal(tfmap)
	return json.Unmarshal(jsonByte, tf)
}

// Option for web
type TokenFieldOption func(o *TokenField)

func AppId(n string) TokenFieldOption {
	return func(o *TokenField) {
		o.AppId = n
	}
}

func UserId(n string) TokenFieldOption {
	return func(o *TokenField) {
		o.UserId = n
	}
}

func Platform(n string) TokenFieldOption {
	return func(o *TokenField) {
		o.Platform = n
	}
}

func DeviceId(n string) TokenFieldOption {
	return func(o *TokenField) {
		o.DeviceId = n
	}
}
func DeviceName(n string) TokenFieldOption {
	return func(o *TokenField) {
		o.DeviceName = n
	}
}

func UserName(n string) TokenFieldOption {
	return func(o *TokenField) {
		o.UserName = n
	}
}

func RealName(n string) TokenFieldOption {
	return func(o *TokenField) {
		o.RealName = n
	}
}

func UserType(n string) TokenFieldOption {
	return func(o *TokenField) {
		o.UserType = n
	}
}

func NewTokenField(opts ...TokenFieldOption) TokenFieldService {
	return retTokenField(opts...)
}

func retTokenField(opts ...TokenFieldOption) *TokenField {
	var opt TokenField
	for _, o := range opts {
		o(&opt)
	}

	return &opt
}

// CreateToken 生成一个token
func CreateToken(uid string) (string, error) {
	claims := &customClaims{}
	claims.UID = uid
	claims.ExpiresAt = time.Now().Add(config.C.Jwt.TimeOut * time.Hour).Unix()
	log.Debug("token内容:", claims)
	token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(SignKey)
	return token, err
}

// CreateToken 生成一个token
func CreateLongToken(uid string) (*customClaims, string, error) {
	claims := &customClaims{}
	claims.UID = uid
	claims.ExpiresAt = time.Now().Add(30 * config.C.Jwt.TimeOut * time.Hour).Unix()
	log.Debug("token内容:", claims)
	token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(SignKey)
	return claims, token, err
}

// 解析Token
func ParseToken(tokenString string) (*customClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &customClaims{}, func(token *jwt.Token) (interface{}, error) {
		return SignKey, nil
	})
	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				return nil, TokenMalformed
			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
				// Token is expired
				return nil, TokenExpired
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				return nil, TokenNotValidYet
			} else {
				return nil, TokenInvalid
			}
		}
	}
	if claims, ok := token.Claims.(*customClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, TokenInvalid
}

// 解析Token
func ParseTokenFromRedis(tokenString string) (*TokenField, error) {
	result, err := redis.HashGetAll(redis.ImRedisDB, "access_token_"+tokenString)
	if err != nil {
		log.Error("从redis中获取 token 失败,err:", err.Error())
		return nil, err
	}
	tf := new(TokenField)
	err = tf.RetStruct(result)
	if err != nil {
		log.Error("反序列化 token 结构体失败，err:", err.Error())
		return tf, err
	}

	if tf == nil {
		err = errors.New("解析 token 结构体失败")
	}
	return tf, err
}

// 更新token
func RefreshToken(tokenString string, appid int) (string, error) {
	jwt.TimeFunc = func() time.Time {
		return time.Unix(0, 0)
	}
	token, err := jwt.ParseWithClaims(tokenString, &customClaims{}, func(token *jwt.Token) (interface{}, error) {
		return SignKey, nil
	})
	if err != nil {
		return "", err
	}
	if claims, ok := token.Claims.(*customClaims); ok && token.Valid {
		jwt.TimeFunc = time.Now
		claims.StandardClaims.ExpiresAt = time.Now().Add(1 * time.Hour).Unix()
		return CreateToken(claims.UID)
	}
	return "", TokenInvalid
}

// OBS 企业服务总线token认证接口返回数据结构
type OBSAuthToken struct {
	Code    int              `json:"code"`
	Data    OBSAuthTokenData `json:"data"`
	Message string           `json:"message"`
}
type OBSAuthTokenData struct {
	ExpiresIn int    `json:"expires_in"`
	Token     string `json:"token"`
}
